#include <mm/mem.h>
#include <kernel/kobject.h>
#include <kernel/initcall.h>
#include <kernel/syslog.h>
#include <drivers/platform.h>
#include <kernel/fdt.h>
#include <drivers/clock.h>


/**
 * @brief simple clock list head
 */
static LIST_HEAD(clock_head);


/**
 * @brief get clock list head
 * @param none
 * @return list head
 */
list_node_t* clock_get_list_head(void)
{
    return &clock_head;
}


/**
 * @brief add clock node to list
 * @param clock: clock node
 * @return none
 */
void clock_node_add(struct clock_node *clock)
{
    list_add_node_at_tail(&clock_head, &clock->link);
}


/**
 * @brief find clock node by name
 * @param name: clock name
 * @return clock node
 */
struct clock_node* clock_find_by_name(const char *name)
{
    struct clock_node *clock = NULL;
    list_for_each_entry(clock, &clock_head, struct clock_node, link) {
        if(strcmp(clock->name, name) == 0) {
            return clock;
        }
    }

    DEBUG_TRACE("clock %s not found", name);
    return NULL;
}


/**
 * @brief enable clock
 * @param clock: clock node
 * @param flag: flag
 * @return none
 */
void clock_enable(struct clock_node *clock, size_t flag)
{
    if(clock->status & (1 << flag)) {
        DEBUG_TRACE("clock %s already enabled, nothing to do", clock->name);
        return;
    }

    if(clock->ops == NULL || clock->ops->enable == NULL) {
        DEBUG_TRACE("clock %s has no enable function", clock->name);
        return;
    }

    if(clock->ops->enable) {
        clock->ops->enable(clock, flag);
    }

    clock->status |= (1 << flag);
}


/**
 * @brief disable clock
 * @param clock: clock node
 * @param flag: flag
 * @return none
 */
void clock_disable(struct clock_node *clock, size_t flag)
{
    if((clock->status & (1 << flag)) == 0) {
        DEBUG_TRACE("clock %s is disabled, nothing to do", clock->name);
        return;
    }

    if(clock->ops == NULL || clock->ops->disable == NULL) {
        DEBUG_TRACE("clock %s has no disable function", clock->name);
        return;
    }

    if(clock->ops->disable) {
        clock->ops->disable(clock, flag);
    }

    clock->status &= ~(1 << flag);
}


/**
 * @brief enable clock by name
 * @param name: clock name
 * @param flag: flag
 * @return none
 */
void clock_enable_by_name(const char *name, size_t flag)
{
    struct clock_node *clock = clock_find_by_name(name);

    if(clock == NULL) {
        return;
    }

    clock_enable(clock, flag);
}


/**
 * @brief disable clock by name
 * @param name: clock name
 * @param flag: flag
 * @return none
 */
void clock_disable_by_name(const char *name, size_t flag)
{
    struct clock_node *clock = clock_find_by_name(name);

    if(clock == NULL) {
        return;
    }

    clock_disable(clock, flag);
}


/**
 * @brief set clock frequency
 * @param clock: clock node
 * @param freq: frequency
 * @return none
 */
void clock_set_freq(struct clock_node *clock, size_t freq)
{
    clock->ops->set_freq(clock, freq);
}


/**
 * @brief get clock frequency
 * @param clock: clock node
 * @return frequency
 */
size_t clock_get_freq(struct clock_node *clock)
{
    return clock->freq;
}


/**
 * @brief set clock frequency by name
 * @param name: clock name
 * @param freq: frequency
 * @return none
 */
void clock_set_freq_by_name(const char *name, size_t freq)
{
    struct clock_node *clock = clock_find_by_name(name);

    if(clock == NULL) {
        return;
    }

    clock_set_freq(clock, freq);
}


/**
 * @brief get clock frequency by name
 * @param name: clock name
 * @return frequency
 */
size_t clock_get_freq_by_name(const char *name)
{
    struct clock_node *clock = clock_find_by_name(name);

    if(clock == NULL) {
        return 0;
    }

    return clock->freq;
}
